package org.noootsab.gae.games.tzaar.model.game {
import com.adobe.serialization.json.JSON;

import mx.controls.Alert;
import mx.logging.ILogger;
import mx.logging.Log;

import org.noootsab.gae.games.tzaar.event.PieceEvent;

public class BoardConfiguration {
    public static const CELLS_BY_LINE:Array = [5, 6, 7, 8, 8, 8, 7, 6, 5 ];
    private static var LOG:ILogger = Log.getLogger("org.noootsab.gae.games.tzaar.model.game.BoardConfiguration");

    public var game:Game;
    public var matrix:Array;
    //array BlackTzaar, BlackTzarra, BlackTott, WhiteTzaar, WhiteTzarra, WhiteTott
    public var piecesCount:Array;

    //used only for generation purpose
    private var pieces:Array = [
        {nb:6, array:[], type:Piece.TZAAR, black:true},
        {nb:9, array:[], type:Piece.TZARRA, black:true},
        {nb:15, array:[], type:Piece.TOTT, black:true},
        {nb:6, array:[], type:Piece.TZAAR, black:false},
        {nb:9, array:[], type:Piece.TZARRA, black:false},
        {nb:15, array:[], type:Piece.TOTT, black:false}
    ];

    public function BoardConfiguration() {
        piecesCount = [0, 0, 0, 0, 0, 0];
    }

    public function init(game:Game, randomize:Boolean = false):void {
        this.game = game;
        if (randomize) {
            generate();
        }
    }

    private function generate():void {
        matrix = new Array();
        piecesCount = [6, 9, 15, 6, 9, 15];
        for (var line:uint = 0; line < 9; line++) {
            matrix[line] = new Array;
            var cells:uint = CELLS_BY_LINE[line];
            for (var cell:uint = 0; cell < cells; cell++) {
                var piece:Piece = pickAPiece(line, cell);
                piece.addEventListener(PieceEvent.STACKED_BY_EVENT, updatePiecesCount);
                piece.addEventListener(PieceEvent.TAKEN_BY_EVENT, updatePiecesCount);
                matrix[line][cell] = piece;
            }
        }
    }

    private function getPiecesCountIndex(p:Piece):uint {
        var index:uint = 0;
        if (!p.black) {
            index += 3;
        }
        switch (p.type) {
            case Piece.TZAAR: index+=0;break;
            case Piece.TZARRA: index+=1;break;
            case Piece.TOTT: index+=2;break;
        }
        return index;
    }

    private function updatePiecesCount(event:PieceEvent):void {
        var p:Piece = event.target as Piece;
        p.removeEventListener(PieceEvent.STACKED_BY_EVENT, updatePiecesCount);
        p.removeEventListener(PieceEvent.TAKEN_BY_EVENT, updatePiecesCount);
        var index:uint = getPiecesCountIndex(p);
        piecesCount[index] = piecesCount[index]-1;
    }

    private function pickAPiece(line:uint, cell:uint):Piece {
        var indexes:Array = [];
        for (var i:uint = 0; i < pieces.length; i++) {
            if (pieces[i].array.length < pieces[i].nb) {
                indexes.push(i);
            }
        }
        var max:int = indexes.length;
        var rnd:uint = Math.random() * max;
        var picked:Object = pieces[indexes[rnd]];
        var p:Piece = new Piece();
        p.init(game, line, cell, picked.type, picked.black);
        picked.array.push(p);
        if (picked == null) {
            throw new Error("No picked piece for line:" + line + " and cell:" + cell);
        }
        return p;
    }

    public function getNeighbors(line:uint, cell:uint):Array {
        //starts at 30 to 330
        return [
            getNeighbor(0, line, cell),
            getNeighbor(1, line, cell),
            getNeighbor(2, line, cell),
            getNeighbor(3, line, cell),
            getNeighbor(4, line, cell),
            getNeighbor(5, line, cell)
        ];

    }

    public function getNeighbor(index:uint, line:uint, cell:uint):Piece {
        var piece:Piece = matrix[line][cell];
        var neighbor:Piece = null;
        var l:uint = line;
        var c:uint = cell;
        while (neighbor === null) {
            var news:Array = getNextNeighbor(index, l, c);
            if (news === null) {
                return null;
            }
            l = news[0];
            c = news[1];
            neighbor = matrix[l][c];
        }
        if (neighbor !== null) {
            if (!checkNeighborValidity(piece, neighbor)) {
                neighbor = null;
            }
        }
        return neighbor;
    }

    private function checkNeighborValidity(piece:Piece, neighbor:Piece):Boolean {
        if (piece.black !== neighbor.black) {
            return piece.strength >= neighbor.strength;
        } else {
            return game.currentState.phase === GameState.CAPTURING_OR_STRENGTHENING_PHASE;
        }
    }

    private function getNextNeighbor(index:uint, line:uint, cell:uint):Array {
        var newLine:uint = line;
        var newCell:uint = cell;
        switch (index) {
            case 0:
                if (line > 4) {
                    if (line != 5 || cell < 4) {
                        newCell++;
                    }
                } else {
                    if (line == 4 && cell >= 4) {
                        newCell++;
                    }
                }
                newLine--;
                if (line == 5 && cell == 3) {
                    return null;
                }
                break;
            case 1:
                newCell++;
                if (line == 4 && cell == 3) {
                    return null;
                }
                break;
            case 2:
                if (line < 4) {
                    if (line != 3 || cell < 4) {
                        newCell++;
                    }
                } else {
                    if (line == 4 && cell >= 4) {
                        newCell++;
                    }
                }
                newLine++;
                if (line == 3 && cell == 3) {
                    return null;
                }
                break;
            case 3:
                if (line >= 4) {
                    if (line != 4 || cell < 4) {
                        newCell--;
                    }
                } else {
                    if (line == 3 && cell >= 4) {
                        newCell--;
                    }
                }
                newLine++;
                if (line == 3 && cell == 4) {
                    return null;
                }
                break;
            case 4:
                newCell--;
                if (line == 4 && cell == 4) {
                    return null;
                }
                break;
            case 5:
                if (line <= 4) {
                    if (line != 4 || cell < 4) {
                        newCell--;
                    }
                } else {
                    if (line == 5 && cell >= 4) {
                        newCell--;
                    }
                }
                newLine--;
                if (line == 5 && cell == 4) {
                    return null;
                }
                break;
        }
        if (newLine < 0 || newLine > 8 || newCell < 0 || newCell >= CELLS_BY_LINE[newLine]) {
            return null;
        }
        return [newLine, newCell];
    }

    public static function fromJSON(game:Game, json:*):BoardConfiguration {
        var array:Array;
        if (json is String) {
            array = JSON.decode(json) as Array;
        } else if (json is Array) {
            array = json;
        } else {
            throw new Error("Bad Json given");
        }

        var configuration:BoardConfiguration = new BoardConfiguration();
        configuration.init(game, false);

        configuration.piecesCount = [0, 0, 0, 0, 0, 0];

        var m:Array = [];
        for (var line:uint = 0; line < array.length; line++) {
            var lineArray:Array = [];
            m.push(lineArray);
            for (var cell:uint = 0; cell < array[line].length; cell++) {
                if (array[line][cell] === null) {
                    lineArray.push(null);
                } else {
                    var piece:Piece = Piece.fromJSON(line, cell, game, array[line][cell]);
                    var index:uint = configuration.getPiecesCountIndex(piece);
                    configuration.piecesCount[index] = configuration.piecesCount[index] + 1;
                    piece.addEventListener(PieceEvent.STACKED_BY_EVENT, configuration.updatePiecesCount);
                    piece.addEventListener(PieceEvent.TAKEN_BY_EVENT, configuration.updatePiecesCount);
                    lineArray.push(piece);

                }
            }
        }
        configuration.matrix = m;
        return configuration;
    }

    public static function toJSON(boardConfiguration:BoardConfiguration):String {
        var json:String = "[";
        var m:Array = boardConfiguration.matrix;
        for (var line:uint = 0; line < m.length; line++) {
            json += "[";
            var lineLg:* = m[line].length;
            for (var cell:uint = 0; cell < lineLg; cell++) {
                if (m[line][cell] === null) {
                    json += "null";
                } else {
                    json += Piece.toJSON(m[line][cell] as Piece);
                }
                if (cell != lineLg - 1) {
                    json += ",";
                }
            }
            json += "]";
            if (line != m.length - 1) {
                json += ",";
            }
        }
        json += "]";
        return json;
    }
}
}